﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rapattern.Utilities
{
    public class SPAnalyHelper
    {
        /// <summary>
        /// 根据SP表分析法 分析学生习题
        /// </summary>
        /// <param name="sp">
        /// 二维数组 第一维代表学生 第二维代表习题， 
        /// 当前值代表该学生在该题目得到的分数
        /// </param>
        public static SPResult SP_Student_Question(int[,] inputdate)
        {
            SPResult model = new SPResult();

            int scount = inputdate.GetLength(0); // 学生数
            int pcount = inputdate.GetLength(1); // 问题数
            int rcount = scount + 2; // 行数 增加第一行保存每个问题答对人数  增加第二行保存每个学生标记
            int ccount = pcount + 2; // 列数 增加第一列保存每个学生答对问题数 增加第二列保存每道习题标记


            int[,] data = new int[rcount, ccount];// 学生与问题的二维数组集合
            int[,] datatemp = new int[rcount, ccount];
            float rightCount = 0; //所有题目答对人数
            float rightRate = 0; // 所有问题的平均正答率
            float subCount = 0; // S线与P线之间的离差面积
            float D = 0; // 差异量
            float d = 0; // 差异系数

            // 统计每个学生总得分和每个问题的答对人数
            for (int i = 0; i < scount; i++)
            {
                for (int j = 0; j < pcount; j++)
                {
                    data[i, j] = inputdate[i, j];
                    if (data[i, j] == 1)
                    {
                        data[i, pcount] += 1;
                        data[scount, j] += 1;
                        // 统计所有学生回答所有问题的正答数 它也等于所有题目答对的人数
                        rightCount += 1;
                    }
                    data[scount + 1, j] = j;// 存储题目标记
                }
                data[i, pcount + 1] = i;// 存储学生标记
            }

            // 按照每个学生得分高低行排序（数组最后一列元素的大小）
            for (int m = 1; m <= scount; m++)
            {
                for (int i = 0; i < scount - m; i++)
                {
                    if (data[i, pcount] < data[i + 1, pcount])
                    {
                        ArrayRowChange<int>(data, i, i + 1);
                    }
                }
            }
            // 按照每个问题的答对人数列排序（数组最后一行元素的大小） 
            for (int n = 1; n <= pcount; n++)
            {
                for (int j = 0; j < pcount - n; j++)
                {
                    if (data[scount, j] < data[scount, j + 1])
                    {
                        ArrayColChange<int>(data, j, j + 1);
                    }
                }
            }

            for (int i = 0; i < scount; i++)
            {
                for (int j = 0; j < data[i, pcount]; j++)
                {
                    datatemp[i, j] = 1;// 根据每个学生的得分判断每行前几列为1
                }
            }
            for (int j = 0; j < pcount; j++)
            {
                for (int i = 0; i < data[scount, j]; i++)
                    datatemp[i, j] = 2;// 根据每个问题的正答数判断每列前几行为2 
            }

            for (int i = 0; i < scount; i++)
            {
                for (int j = 0; j < pcount; j++)
                    if (datatemp[i, j] == 1)
                        subCount += 1;// 数组b中的元素有1有2，先统计此时1(与2不重叠)的个数            
            }

            for (int i = 0; i < scount; i++)
            {
                for (int j = 0; j < data[i, pcount]; j++)
                    datatemp[i, j] = 1;// 重新对数组b按照学生得分赋值1（为了统计不重叠的2的个数） 
            }

            for (int i = 0; i < scount; i++)
            {
                for (int j = 0; j < pcount; j++)
                    if (datatemp[i, j] == 2)
                        subCount += 1;// 在统计1的个数基础之上加不重叠的2的个数（实现SP所包含的元素的个数）   
            }

            rightRate = rightCount / (scount * pcount);// 所有问题的平均正答率 
            D = subCount / (scount * pcount);// 差异量    

            /*差异系数计算公式
             *         0.7*差异量
             * d = --------------------
             *      正答率*(1-正答率)
             */
            d = 0.7f * D / (rightRate * (1 - rightRate));// 差异系数

            Console.WriteLine($"q={D},d={d}");

            for (int i = 0; i < scount; i++)
            {
                float p0 = 0; float p1 = 0; float pa = 0;
                for (int j = 0; j < data[i, pcount]; j++)
                {
                    if (data[i, j] == 0)
                        p0 = p0 + data[scount, j];// 根据学生i的总得分为临界点,位于S曲线左侧应答结果为0的诸问题的正答人数之和
                }
                for (int j = data[i, pcount]; j < pcount; j++)
                {
                    if (data[i, j] == 1)
                        p1 = p1 + data[scount, j];// 根据学生i的总得分为临界点,位于S曲线右侧应答结果为1的诸问题的正答人数之和
                }
                for (int j = 0; j < data[i, pcount]; j++)
                {
                    pa = pa + data[scount, j];// 根据学生i的总得分为临界点 求S线左侧每一个问题正答人数之和
                }

                /*
                 *              |学生i的得分模式中，位于|   |学生i的得分模式中，位于|
                 *              |S曲线左侧应答结果为0的 | - |S曲线右侧应答结果为1的 |
                 *              |诸问题的正答人数之和   |   |诸问题的正答人数之和   |            
                 * cs = ------------------------------------------------------------------
                 *          |学生i的得分模式中，|   |           |   |                    |
                 *          |位于S曲线左侧每一  | - |学生i的总分| * |每个学生的平均正答数|                 
                 *          |个问题正答人数之和 |   |           |   |                    | 
                 */
                float divisor = (pa - data[i, pcount] * rightCount / pcount);
                float cs = divisor != 0 ? (p0 - p1) / divisor : 0;  // 每个学生的注意系数计算公式
                float pr = data[i, pcount] / (float)pcount; // 该学生的答对率
                SPItem result = GetStudentDiagnose(cs, pr);
                result.Num = data[i, pcount + 1];
                model.StudentList.Add(result);
            }

            for (int j = 0; j < pcount; j++)
            {
                float s0 = 0; float s1 = 0; float sa = 0;
                for (int i = 0; i < data[scount, j]; i++)
                {
                    if (data[i, j] == 0)
                        s0 = s0 + data[i, pcount];// 根据J问题的答对人数为临界点 位于P曲线上侧应答结果为0的各个学生总成绩之和
                }
                for (int i = data[scount, j]; i < scount; i++)
                {
                    if (data[i, j] == 1)
                        s1 = s1 + data[i, pcount];// 根据J问题的答对人数为临界点 位于P曲线下侧应答结果为1的各个学生总成绩之和
                }
                for (int i = 0; i < data[scount, j]; i++)
                {
                    sa = sa + data[i, pcount];// 第j个问题得分模式中，位于P曲线上侧各个学生总分之和
                }
                /*
                 *              |第j个问题得分模式中,位|   |第j个问题得分模式中,位|
                 *              |于P曲线上侧应答结果为 | - |于P曲线下侧应答结果为 |
                 *              |0的各个学生总成绩之和 |   |1的各个学生总成绩之和 |            
                 * cp = ------------------------------------------------------------------
                 *                |第j个问题得分模式|   |问题j的正|   |每个学生的|
                 *                |中,位于P曲线上侧 | - |         | * |          |                 
                 *                |各个学生总分之和 |   |答者人数 |   |平均分数  | 
                 */
                float divisor = (sa - data[scount, j] * rightCount / scount);
                float cp = divisor != 0 ? (s0 - s1) / divisor : 0;  //每个问题的注意系数计算公式
                float sr = data[scount, j] / (float)scount; // 问题的答对率
                SPItem result = GetQuestionDiagnose(cp, sr);
                result.Num = data[scount + 1, j];
                model.QuestionList.Add(result);
            }

            return model;
        }

        /// <summary>
        /// 二维数组 两行之间相互交换
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="row1"></param>
        /// <param name="row2"></param>
        private static void ArrayRowChange<T>(T[,] data, int row1, int row2)
        {
            for (int i = 0; i < data.GetLength(1); i++)
            {
                var temp = data[row1, i];
                data[row1, i] = data[row2, i];
                data[row2, i] = temp;
            }
        }

        /// <summary>
        /// 二维数组 两列之间相互交换
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="row1"></param>
        /// <param name="row2"></param>
        private static void ArrayColChange<T>(T[,] data, int col1, int col2)
        {
            for (int i = 0; i < data.GetLength(0); i++)
            {
                var temp = data[i, col1];
                data[i, col1] = data[i, col2];
                data[i, col2] = temp;
            }
        }

        /// <summary>
        /// 获取学生类别诊断
        /// </summary>
        /// <param name="cs"></param>
        /// <param name="pr"></param>
        /// <returns></returns>
        private static SPItem GetStudentDiagnose(float cs, float pr)
        {
            SPItem item = new SPItem();
            item.Accuracy = pr;
            if (cs >= 0.50 && pr >= 0.75)
            { //A'
                item.Region = "A'";
                item.Describe = "优秀，稳定性不足。需克服粗心大意。";
            }
            else if (cs >= 0.00 && pr >= 0.75)
            { //A
                item.Region = "A";
                item.Describe = "优秀，稳定性高。可挑战更高难度。";
            }
            else if (cs >= 0.50 && pr >= 0.50)
            { //B'
                item.Region = "B'";
                item.Describe = "良好，稳定性不足。需克服粗心大意。";
            }
            else if (cs >= 0.00 && pr >= 0.50)
            { //B
                item.Region = "B";
                item.Describe = "良好，稳定性高。需提高学习难度。";
            }
            else if (cs >= 0.50 && pr >= 0.00)
            { // C'
                item.Region = "C'";
                item.Describe = "有潜力，稳定性不足。需努力夯实基础。";
            }
            else if (cs >= 0.00 & pr >= 0.00)
            { // C
                item.Region = "C";
                item.Describe = "有潜力，准备不足。需努力夯实基础。";
            }
            else
            {
                item.Region = "C";
                item.Describe = "有潜力，准备不足。需努力夯实基础。";
            }

            return item;
        }
        /// <summary>
        /// 获取习题类别诊断
        /// </summary>
        /// <param name="cp"></param>
        /// <param name="qdl"></param>
        private static SPItem GetQuestionDiagnose(float cp, float sr)
        {
            SPItem item = new SPItem();
            item.Accuracy = sr;
            if (cp >= 0.50 && sr >= 0.70)
            {
                item.Region = "A'";
                item.Describe = "易答异质试题";
                item.Level = 1;
            }
            else if (cp >= 0.00 && sr >= 0.70)
            {
                item.Region = "A";
                item.Describe = "易答试题";
                item.Level = 2;
            }
            else if (cp >= 0.50 && sr >= 0.30)
            {
                item.Region = "B'";
                item.Describe = "中等难度易错试题";
                item.Level = 3;
            }
            else if (cp >= 0.00 && sr >= 0.30)
            {
                item.Region = "B";
                item.Describe = "中等难度试题";
                item.Level = 4;
            }
            else if (cp >= 0.50 && sr >= 0.00)
            {
                item.Region = "C'";
                item.Describe = "高难度异质试题";
                item.Level = 5;
            }
            else if (cp >= 0.00 & sr >= 0.00)
            {
                item.Region = "C";
                item.Describe = "高难度试题";
                item.Level = 6;
            }
            else
            {
                item.Region = "";
                item.Describe = "高难度试题";
                item.Level = 7;
            }

            return item;
        }
    }
    public class SPResult
    {
        public List<SPItem> StudentList { get; set; }
        public List<SPItem> QuestionList { get; set; }

        public SPResult()
        {
            StudentList = new List<SPItem>();
            QuestionList = new List<SPItem>();
        }
    }

    public class SPItem
    {
        /// <summary>
        /// 习题标志/学生标志
        /// </summary>
        public int Num { get; set; }
        /// <summary>
        /// 级别
        /// </summary>
        public string Region { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        public string Describe { get; set; }
        /// <summary>
        /// 答对率 例 0.+
        /// </summary>
        public float Accuracy { get; set; }
        /// <summary>
        /// 难度级别 主要用于排序
        /// </summary>
        public int Level { get; set; }
    }
}
